import { StorageManager } from "@/managers/StorageManager.js";
import { Game_Temp } from "@/objects/GameTemp.js";
import { Game_System } from "@/objects/GameSystem.js";
import { Game_Screen } from "@/objects/GameScreen.js";
import { Game_Timer } from "@/objects/GameTimer.js";
import { Game_Message } from "@/objects/GameMessage.js";
import { Game_Switches } from "@/objects/GameSwitches.js";
import { Game_Variables } from "@/objects/GameVariables.js";
import { Game_SelfSwitches } from "@/objects/GameSelfSwitches.js";
import { Game_Actors } from "@/objects/GameActors.js";
import { Game_Party } from "@/objects/GameParty.js";
import { Game_Troop } from "@/objects/GameTroop.js";
import { Game_Map } from "@/objects/GameMap.js";
import { Game_Player } from "@/objects/GamePlayer.js";
import { DateTool } from "@/utils/DateTool.js";

export class DataManager {
    static loadingTaskCount = 0;
    static isMapLoading = false;

    static loadDatabase() {

        let dbs = [
            { name: '$dataActors', src: '/data/Actors.json' },
            { name: '$dataClasses', src: '/data/Classes.json' },
            { name: '$dataSkills', src: '/data/Skills.json' },
            { name: '$dataItems', src: '/data/Items.json' },
            { name: '$dataWeapons', src: '/data/Weapons.json' },
            { name: '$dataArmors', src: '/data/Armors.json' },
            { name: '$dataEnemies', src: '/data/Enemies.json' },
            { name: '$dataTroops', src: '/data/Troops.json' },
            { name: '$dataStates', src: '/data/States.json' },
            { name: '$dataAnimations', src: '/data/Animations.json' },
            { name: '$dataTilesets', src: '/data/Tilesets.json' },
            { name: '$dataCommonEvents', src: '/data/CommonEvents.json' },
            { name: '$dataSystem', src: '/data/System.json' },
            //{ name: '$dataMapInfos', src: '/data/MapInfos.json' }
        ]
        
        for (let db of dbs) {
            this.loadDataFile(db.name, db.src);
        }
    }

    static destroy(){
        window.$dataActors = null;
        window.$dataClasses = null;
        window.$dataSkills = null;
        window.$dataItems = null;
        window.$dataWeapons = null;
        window.$dataArmors = null;
        window.$dataEnemies = null;
        window.$dataTroops = null;
        window.$dataStates = null;
        window.$dataAnimations = null;
        window.$dataTilesets = null;
        window.$dataCommonEvents = null;
        window.$dataSystem = null;
        window.$dataMapInfos = null;
    }

    static loadDataFile(name, src) {
        ++this.loadingTaskCount;
        fetch(src)
            .then(response => response.json())
            .then(json => {
                
                window[name] = json;
                let isMap = false;
                let array = null;
                if (json == window.$dataMap) {
                    this.extractMetadata(json);
                    array = json.events;
                    isMap = true;
                } else {
                    array = json;
                }
                if (Array.isArray(array)) {
                    for (let data of array) {
                        if (data?.note) {
                            this.extractMetadata(data);
                        }
                    }
                }
                this.isMapLoading = !isMap;
                if (json === window.$dataSystem) {
                    //Scene_Boot.loadSystemImages();
                }
                --this.loadingTaskCount;
            });
    }

    static isDatabaseLoaded() {
        return this.loadingTaskCount == 0;
    }

    static loadMapData(mapId) {
        if (mapId > 0) {
            this.isMapLoading = true;
            this.loadDataFile('$dataMap', `/data/Map${String(mapId).padStart(3, '0')}.json`);
        } else {
            this.makeEmptyMap();
        }
    }

    static makeEmptyMap() {
        $dataMap = {};
        $dataMap.data = [];
        $dataMap.events = [];
        $dataMap.width = 100;
        $dataMap.height = 100;
        $dataMap.scrollType = 3;
    }

    static isMapLoaded() {
        return !this.isMapLoading;
    }

    static extractMetadata(data) {
        data.meta = {};
        let matchAll = data.note.matchAll(/<([^<>:]+)(:?)([^>]*)>/g);
        for (let [str, key, op, value] of matchAll) {
            data.meta[key] = op == ':' ? value : true;
        }
    }

    static isSkill(item) {
        return item && $dataSkills[item.id] == item;
    }

    static isItem(item) {
        return item && $dataItems[item.id] == item;
    }

    static isWeapon(item) {
        return item && $dataWeapons[item.id] == item;
    }

    static isArmor(item) {
        return item && $dataArmors[item.id] == item;
    }

    static createGameObjects() {
        window.$gameTemp = new Game_Temp();
        window.$gameSystem = new Game_System();
        window.$gameScreen = new Game_Screen();
        window.$gameTimer = new Game_Timer();
        window.$gameMessage = new Game_Message();
        window.$gameSwitches = new Game_Switches();
        window.$gameVariables = new Game_Variables();
        window.$gameSelfSwitches = new Game_SelfSwitches();
        window.$gameActors = new Game_Actors();
        window.$gameParty = new Game_Party();
        window.$gameTroop = new Game_Troop();
        window.$gameMap = new Game_Map();
        window.$gamePlayer = new Game_Player();
    }

    static setupNewGame() {
        this.createGameObjects();
        $gameParty.setupStartingMembers();
        $gamePlayer.reserveTransfer(
            $dataSystem.startMapId,
            $dataSystem.startX, $dataSystem.startY
        );
    }

    static setupBattleTest() {
        this.createGameObjects();
        $gameParty.setupBattleTest();
        BattleManager.setup($dataSystem.testTroopId, true, false);
        BattleManager.setBattleTest(true);
        BattleManager.playBattleBgm();
    }

    static setupEventTest() {
        this.createGameObjects();
        $gameParty.setupStartingMembers();
        $gamePlayer.reserveTransfer(-1, 8, 6);
        $gamePlayer.setTransparent(false);
    }

    static maxSavefiles() {
        return 20;
    }

    static saveGame(savefileId) {
        StorageManager.save(
            savefileId, 
            JSON.stringify(this.makeSaveContents()));
    }

    static loadGame(savefileId) {
        let json = StorageManager.load(savefileId);
        if(!json){
            throw new Error('loadGame failed');
        }
        this.createGameObjects();
        this.extractSaveContents(JSON.parse(json));
    }

    static makeGlobalInfo() {
        let info = {};
        info.characters = $gameParty.charactersForSavefile();
        //info.faces = $gameParty.facesForSavefile();
        info.playtime = $gameSystem.playtimeText();
        //info.timestamp = Date.now();
        info.date=DateTool.localDateString();
        return info;
    }

    static makeSaveContents() {
        // A save data does not contain $gameTemp, $gameMessage, and $gameTroop.
        return {
            system:$gameSystem,
            timer:$gameTimer,
            switches:$gameSwitches,
            selfSwitches:$gameSelfSwitches,
            variables:$gameVariables,
            screen:$gameScreen.serialize(),
            actors:$gameActors.serialize(),
            party:$gameParty.serialize(),
            map:$gameMap.serialize(),
            player:$gamePlayer.serialize()
        };
    }

    static extractSaveContents(contents) {
        Object.assign($gameSystem, contents.system);
        Object.assign($gameTimer, contents.timer);
        Object.assign($gameSwitches, contents.switches);
        Object.assign($gameVariables, contents.variables);
        Object.assign($gameSelfSwitches, contents.selfSwitches);
        $gameScreen.deserialize(contents.screen);
        $gameActors.deserialize(contents.actors);
        $gameParty.deserialize(contents.party);
        $gameMap.deserialize(contents.map);
        $gamePlayer.deserialize(contents.player);
    }
}